/*
 * IpcMessageQueue.cpp
 *
 *  Created on: Oct 31, 2013
 *      Author: vladimir
 */

#include "IpcMessageQueue.hpp"

#include "constants.hpp"

#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>

#include <sstream>
#include <utility>
#include <cerrno>
#include <cstring>
#include <fstream>

namespace mq
{

namespace
{

void ThrowExceptionForError( const std::string & txt, const int errorNumber )
{
	std::stringstream ss;
	ss << txt << " errno(" << errorNumber << ") strerror(" << strerror( errorNumber ) << ")";
	throw ss.str();
}

mq_attr GetMqAttributes()
{
	mq_attr attr;

    attr.mq_flags = 0;
    attr.mq_maxmsg = MAX_NO_MESSAGES_IN_QUEUE;
    attr.mq_msgsize = MAX_MESSAGE_SIZE;
    attr.mq_curmsgs = 0;

	return attr;
}

}

struct IpcMessageQueue::Imp
{
	Imp( const std::string & name );
	~Imp();

	std::string mqName;
	mqd_t mqDesc;
};

IpcMessageQueue::Imp::Imp( const std::string & name ) :
		mqName( "/" + name ),
		mqDesc( -1 )
{
	mq_attr attr = GetMqAttributes();

	mqDesc = mq_open( mqName.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, &attr );

	if ( -1 == mqDesc )
	{
		const std::string errMsg( "Error opening or creating a message queue (" + mqName + ")." );
		ThrowExceptionForError( errMsg, errno );
	}
}

IpcMessageQueue::Imp::~Imp()
{
	mq_close( mqDesc );
	mq_unlink( mqName.c_str() );
}


IpcMessageQueue::IpcMessageQueue( const std::string & name ) :
		pimpl( new Imp( name ) )
{
}

IpcMessageQueue::~IpcMessageQueue()
{
}

IpcMessageQueue::IpcMessageQueue(IpcMessageQueue&& rhs) :
		pimpl( std::move( rhs.pimpl ) )
{
}

IpcMessageQueue& IpcMessageQueue::operator =(IpcMessageQueue&& rhs)
{
	pimpl = std::move( rhs.pimpl );

	return *this;
}

void IpcMessageQueue::SendMessage(
		const char* data,
		const unsigned int dataLength )
{
	const int sendResult = mq_send( pimpl->mqDesc, data, dataLength, 0 );

	if ( -1 == sendResult )
	{
		ThrowExceptionForError( "Failed to send a message.", errno );
	}
}

unsigned int IpcMessageQueue::ReceiveMessage(
		char* buffer,
		const unsigned int bufferSize)
{
	const auto receiveResult = mq_receive( pimpl->mqDesc, buffer, bufferSize, 0 );

	if ( -1 == receiveResult )
	{
		if ( EAGAIN == errno )
		{
			return 0;
		}

		ThrowExceptionForError( "Failed to receive a message.", errno );
	}

	return receiveResult;
}

} /* namespace mq */
